package com.spring;

import java.io.File;
import java.io.UnsupportedEncodingException;
import java.lang.annotation.Annotation;

import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.net.URL;
import java.net.URLDecoder;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;

/**
 * @author xuxin
 * @create 2024-10-08 22:10
 */

public class ZhouyuApplicationContext {


    private Class configClass;

    private ConcurrentHashMap<String, Object> singletonObjects = new ConcurrentHashMap<>();

    private ConcurrentHashMap<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<>();

    private List<BeanPostProcessor> beanPostProcessorList = new ArrayList<>();


    public ZhouyuApplicationContext(Class configClass) throws ClassNotFoundException {
        this.configClass = configClass;

        //解析配置类
        //ComponentScan注解--> 扫描路径 --> 扫描 --> 生成beanDefinitation对象 --> 放进beanDefinitationMap中
        scan(configClass);

        Set<Map.Entry<String, BeanDefinition>> entrySet = beanDefinitionMap.entrySet();
        for (Map.Entry<String, BeanDefinition> entry : entrySet) {
            String beanName = entry.getKey();
            BeanDefinition beanDefinition = entry.getValue();
            if ("singleton".equals(beanDefinition.getScope())) {
                Object bean = createBean(beanName, beanDefinition);
                singletonObjects.put(beanName, bean);
            }
        }

    }

    private Object createBean(String beanName, BeanDefinition beanDefinition) {
        //从beanDefinitation中获得该类
        Class clazz = beanDefinition.getClazz();
        Object o = null;
        try {
            //调构造方法创建一个实例
            o = clazz.getDeclaredConstructor().newInstance();
            //遍历所有属性
            Field[] declaredFields = clazz.getDeclaredFields();
            for (Field declaredField : declaredFields) {
                if (declaredField.isAnnotationPresent(AutoWired.class)) {
                    //如果某属性具有AutoWired注解 调用getBean 根据beanName获取
                    Object bean = getBean(declaredField.getName());
                    declaredField.setAccessible(true);
                    declaredField.set(o, bean);
                }
            }

            //aware回调
            if (o instanceof BeanNameAware) {
                ((BeanNameAware) o).setBeanName(beanName);
            }

            //初始化之前执行postProcessBeforeInitialization
            for (BeanPostProcessor beanPostProcessor : beanPostProcessorList) {
                o = beanPostProcessor.postProcessBeforeInitialization(o, beanName);
            }

            //初始化
            if (o instanceof  InitializingBean) {
                ((InitializingBean) o).afterPropertiesSet();
            }

            //初始化之后执行postProcessBeforeInitialization
            for (BeanPostProcessor beanPostProcessor : beanPostProcessorList) {
                o = beanPostProcessor.postProcessAfterInitialization(o, beanName);
            }

        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return o;
    }

    private void scan(Class configClass) {
        if (configClass.isAnnotationPresent(ComponentScan.class)) {
            ComponentScan componentScanAnnotation = (ComponentScan) configClass.getDeclaredAnnotation(ComponentScan.class);
            String scanPath = componentScanAnnotation.value();
            System.out.println(scanPath);
            ClassLoader classLoader = ZhouyuApplicationContext.class.getClassLoader();
            URL resource = classLoader.getResource(scanPath.replace('.', '\\'));
            String scanDir = null;
            try {
                scanDir = URLDecoder.decode(resource.getPath(), StandardCharsets.UTF_8.name());
            } catch (UnsupportedEncodingException e) {
                e.printStackTrace();
            }
            //扫描路径下所有的文件
            File file = new File(scanDir);
            ArrayList<String> classList = new ArrayList<>();
            if (file.isDirectory()) {
                scanFile(file, classList);
            }
            System.out.println(classList);
            //解析每个类是否需要生成对象
            for (String clazz : classList) {
                String classPath = clazz.substring(clazz.indexOf("com"), clazz.indexOf(".class"));
                classPath = classPath.replace('\\', '.');
                try {
                    Class<?> aClass = classLoader.loadClass(classPath);
                    //是否具有@Component注解
                    if (aClass.isAnnotationPresent(Component.class)) {

                        //获取实现了BeanPostProcessor的类 存到beanPostProcessorList 方便后续创建普通bean的时候使用
                        if (BeanPostProcessor.class.isAssignableFrom(aClass)) {
                            BeanPostProcessor instance = (BeanPostProcessor) aClass.getDeclaredConstructor().newInstance();
                            beanPostProcessorList.add(instance);
                        }

                        //表示当前这个类是一个Bean
                        Component componentAnnotation = aClass.getDeclaredAnnotation(Component.class);
                        String beanName = componentAnnotation.value();
                        BeanDefinition beanDefinition = new BeanDefinition();
                        beanDefinition.setClazz(aClass);
                        if (aClass.isAnnotationPresent(Scope.class)) {
                            Scope scopeAnnotation = aClass.getDeclaredAnnotation(Scope.class);
                            beanDefinition.setScope(scopeAnnotation.value());
                        }else {
                            //单例
                            beanDefinition.setScope("singleton");
                        }
                        beanDefinitionMap.put(beanName, beanDefinition);
                    }
                } catch (ClassNotFoundException | NoSuchMethodException e) {
                    e.printStackTrace();
                } catch (InvocationTargetException e) {
                    e.printStackTrace();
                } catch (InstantiationException e) {
                    e.printStackTrace();
                } catch (IllegalAccessException e) {
                    e.printStackTrace();
                }
            }


        }
    }

    private void scanFile(File file, ArrayList<String> classList) {
        if (file.isDirectory()) {
            File[] files = file.listFiles();
            for (File f : files) {
                if (f.isDirectory()) {
                    scanFile(f, classList);
                }else {
                    classList.add(f.getAbsolutePath());
                }
            }
        }
    }


    public Object getBean(String beanName) throws Exception {
        if (beanDefinitionMap.containsKey(beanName)) {
            BeanDefinition beanDefinition = beanDefinitionMap.get(beanName);
            if ("singleton".equals(beanDefinition.getScope())) {
                //单例
                return singletonObjects.get(beanName);
            }else if ("prototype".equals(beanDefinition.getScope())){
                //多例
                Object bean = createBean(beanName, beanDefinition);
                return bean;
            }else {
                throw new Exception("非单例或者多例");
            }
        }else {
            throw new ClassNotFoundException("没有该Bean");
        }
    }
}
